﻿namespace NServiceBus.AcceptanceTesting;

using System;
using System.Diagnostics;
using Logging;
using NUnit.Framework;

class ContextAppender(string logger) : ILog
{
    public bool IsDebugEnabled => CurrentLogLevel <= LogLevel.Debug;
    public bool IsInfoEnabled => CurrentLogLevel <= LogLevel.Info;
    public bool IsWarnEnabled => CurrentLogLevel <= LogLevel.Warn;
    public bool IsErrorEnabled => CurrentLogLevel <= LogLevel.Error;
    public bool IsFatalEnabled => CurrentLogLevel <= LogLevel.Fatal;

    public void Debug(string? message) => Log(message, LogLevel.Debug);

    public void Debug(string? message, Exception? exception)
    {
        var fullMessage = $"{message} {exception}";
        Log(fullMessage, LogLevel.Debug);
    }

    public void DebugFormat(string format, params object?[] args)
    {
        var fullMessage = string.Format(format, args);
        Log(fullMessage, LogLevel.Debug);
    }

    public void Info(string? message) => Log(message, LogLevel.Info);

    public void Info(string? message, Exception? exception)
    {
        var fullMessage = $"{message} {exception}";
        Log(fullMessage, LogLevel.Info);
    }

    public void InfoFormat(string format, params object?[] args)
    {
        var fullMessage = string.Format(format, args);
        Log(fullMessage, LogLevel.Info);
    }

    public void Warn(string? message) => Log(message, LogLevel.Warn);

    public void Warn(string? message, Exception? exception)
    {
        var fullMessage = $"{message} {exception}";
        Log(fullMessage, LogLevel.Warn);
    }

    public void WarnFormat(string format, params object?[] args)
    {
        var fullMessage = string.Format(format, args);
        Log(fullMessage, LogLevel.Warn);
    }

    public void Error(string? message) => Log(message, LogLevel.Error);

    public void Error(string? message, Exception? exception)
    {
        var fullMessage = $"{message} {exception}";
        Log(fullMessage, LogLevel.Error);
    }

    public void ErrorFormat(string format, params object?[] args)
    {
        var fullMessage = string.Format(format, args);
        Log(fullMessage, LogLevel.Error);
    }

    public void Fatal(string? message) => Log(message, LogLevel.Fatal);

    public void Fatal(string? message, Exception? exception)
    {
        var fullMessage = $"{message} {exception}";
        Log(fullMessage, LogLevel.Fatal);
    }

    public void FatalFormat(string format, params object?[] args)
    {
        var fullMessage = string.Format(format, args);
        Log(fullMessage, LogLevel.Fatal);
    }

    void Log(string? message, LogLevel messageSeverity)
    {
        var context = ScenarioContext.Current;
        if (context == null)
        {
            // avoid NRE in case something logs outside of a test scenario
            TestContext.Out.WriteLine(message);
            return;
        }

        if (context.LogLevel > messageSeverity)
        {
            return;
        }

        Trace.WriteLine(message);
        context.Logs.Enqueue(new ScenarioContext.LogItem
        {
            Endpoint = ScenarioContext.CurrentEndpoint,
            LoggerName = logger,
            Level = messageSeverity,
            Message = message
        });
    }

    static LogLevel CurrentLogLevel => ScenarioContext.Current?.LogLevel ?? LogLevel.Debug;
}